package org.Shiikong_Memory.SakuraBlockFrame.DataTool;

import org.Shiikong_Memory.SakuraBlockFrame.Abnormal.NoInitAbnomal;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * 一个泛型类，用于处理与 MySQL 数据库的交互。该类提供了数据库配置、表创建、数据插入、查询、更新和删除等功能。
 *
 * @param <T> 实体类的类型，用于映射数据库表的记录。
 *
 * @author Shiikong_Memory
 * @version 1.0
 */
public class MysqlTool<T> {
    private static boolean isConnect = false;
    /**
     * 数据库连接对象，静态变量，所有 MysqlTool 实例共享同一个连接。
     */
    private static Connection connection;
    /**
     * 实体类的 Class 对象，用于反射获取实体类的属性信息。
     */
    private final Class<T> entityClass;

    /**
     * 配置数据库连接信息。
     *
     * @param url      数据库连接 URL。
     * @param user     数据库用户名。
     * @param password 数据库密码。
     * @throws SQLException 当数据库连接失败时抛出此异常。
     */
    public static void configure(String url, String user, String password) throws SQLException {
        connection = DriverManager.getConnection(url, user, password);
        isConnect = true;
    }

    /**
     * 构造函数，初始化 MysqlTool 实例，并在必要时创建数据库表。
     *
     * @param entityClass 实体类的 Class 对象。
     * @throws NoInitAbnomal 当数据库工具为初始化时抛出此异常
     */
    public MysqlTool(Class<T> entityClass) throws NoInitAbnomal{
        this.entityClass = entityClass;
        createTableIfNotExists();
        if (!isConnect) {
            throw new NoInitAbnomal("Mysql工具");
        }
    }

    /**
     * 如果数据库中不存在对应的表，则创建该表。表结构基于实体类的注解信息。
     */
    private void createTableIfNotExists() {
        StringBuilder sql = new StringBuilder("CREATE TABLE IF NOT EXISTS ")
                .append(getTableName()).append(" (");

        Field[] fields = entityClass.getDeclaredFields();
        for (Field field : fields) {
            field.setAccessible(true);
            sql.append(field.getName()).append(" ").append(getSqlType(field));

            if (field.isAnnotationPresent(PrimaryKey.class)) {
                sql.append(" PRIMARY KEY");
                if (field.getAnnotation(PrimaryKey.class).autoIncrement()) {
                    sql.append(" AUTO_INCREMENT");
                }
            }
            sql.append(", ");
        }
        sql.delete(sql.length()-2, sql.length()).append(")");

        executeUpdate(sql.toString());
    }

    /**
     * 向数据库表中插入一条记录。
     *
     * @param entity 要插入的实体对象。
     * @return 插入操作影响的行数，如果发生异常则返回 -1。
     */
    public int insert(T entity) {
        StringBuilder sql = new StringBuilder("INSERT INTO ")
                .append(getTableName()).append(" (");
        StringBuilder values = new StringBuilder("VALUES (");

        Field[] fields = entityClass.getDeclaredFields();
        try {
            for (Field field : fields) {
                if (field.isAnnotationPresent(AutoIncrement.class)) continue;

                sql.append(field.getName()).append(", ");
                values.append("?, ");
            }
            sql.delete(sql.length()-2, sql.length()).append(") ");
            values.delete(values.length()-2, values.length()).append(")");

            PreparedStatement ps = connection.prepareStatement(sql.append(values).toString());

            int index = 1;
            for (Field field : fields) {
                if (field.isAnnotationPresent(AutoIncrement.class)) continue;

                field.setAccessible(true);
                ps.setObject(index++, field.get(entity));
            }
            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 查询数据库表中的所有记录。
     *
     * @return 包含所有记录对应的实体对象的列表，如果发生异常则返回空列表。
     */
    public List<T> selectAll() {
        List<T> result = new ArrayList<>();
        String sql = "SELECT * FROM " + getTableName();

        try (Statement stmt = connection.createStatement();
             ResultSet rs = stmt.executeQuery(sql)) {

            while (rs.next()) {
                T entity = entityClass.getDeclaredConstructor().newInstance();
                for (Field field : entityClass.getDeclaredFields()) {
                    field.setAccessible(true);
                    field.set(entity, rs.getObject(field.getName()));
                }
                result.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 根据自定义 SQL 语句和参数查询数据库表中的记录。
     *
     * @param sql    自定义的 SQL 语句，会自动添加表名前缀。
     * @param params SQL 语句的参数。
     * @return 包含查询结果对应的实体对象的列表，如果发生异常则返回空列表。
     */
    public List<T> selectBySql(String sql, Object... params) {
        List<T> result = new ArrayList<>();
        sql = "SELECT * FROM " + getTableName()+ " " + sql;
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            // 设置参数
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }

            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                T entity = entityClass.getDeclaredConstructor().newInstance();
                // 根据结果集列名设置字段值
                ResultSetMetaData metaData = rs.getMetaData();
                for (int i = 1; i <= metaData.getColumnCount(); i++) {
                    String columnName = metaData.getColumnName(i);
                    Object value = rs.getObject(columnName);

                    // 查找匹配的字段
                    for (Field field : entityClass.getDeclaredFields()) {
                        if (field.getName().equalsIgnoreCase(columnName)) {
                            field.setAccessible(true);
                            field.set(entity, value);
                            break;
                        }
                    }
                }
                result.add(entity);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    /**
     * 更新数据库表中的一条记录。
     *
     * @param entity 包含更新信息的实体对象。
     * @return 更新操作影响的行数，如果发生异常则返回 -1。
     */
    public int update(T entity) {
        StringBuilder sql = new StringBuilder("UPDATE ")
                .append(getTableName()).append(" SET ");
        String where = "";

        try {
            // 构建SET子句
            for (Field field : entityClass.getDeclaredFields()) {
                if (field.isAnnotationPresent(PrimaryKey.class)) {
                    where = " WHERE " + field.getName() + "=?";
                    continue;
                }
                sql.append(field.getName()).append("=?, ");
            }
            sql.delete(sql.length()-2, sql.length()).append(where);

            PreparedStatement ps = connection.prepareStatement(sql.toString());

            int index = 1;
            Object primaryValue = null;
            // 设置SET参数
            for (Field field : entityClass.getDeclaredFields()) {
                if (field.isAnnotationPresent(PrimaryKey.class)) {
                    primaryValue = field.get(entity);
                    continue;
                }
                ps.setObject(index++, field.get(entity));
            }
            // 设置WHERE参数
            ps.setObject(index, primaryValue);

            return ps.executeUpdate();
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 根据主键值删除数据库表中的一条记录。
     *
     * @param primaryKey 要删除记录的主键值。
     * @return 删除操作影响的行数，如果发生异常则返回 -1。
     */
    public int delete(Object primaryKey) {
        String sql = "DELETE FROM " + getTableName() + " WHERE " +
                getPrimaryKeyName() + "=?";

        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            ps.setObject(1, primaryKey);
            return ps.executeUpdate();
        } catch (SQLException e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 获取实体类对应的数据库表名，表名默认为实体类名的小写形式。
     *
     * @return 数据库表名。
     */
    private String getTableName() {
        return entityClass.getSimpleName().toLowerCase();
    }

    /**
     * 获取实体类对应的数据库表的主键字段名。
     *
     * @return 主键字段名，如果未找到则返回 "null"。
     */
    private String getPrimaryKeyName() {
        for (Field field : entityClass.getDeclaredFields()) {
            if (field.isAnnotationPresent(PrimaryKey.class)) {
                return field.getName();
            }
        }
        return "null";
    }

    /**
     * 根据实体类的字段类型获取对应的 SQL 类型。
     *
     * @param field 实体类的字段。
     * @return 对应的 SQL 类型字符串。
     */
    private String getSqlType(Field field) {
        Class<?> type = field.getType();
        if (type == String.class) return "VARCHAR(255)";
        if (type == int.class || type == Integer.class) return "INT";
        if (type == long.class || type == Long.class) return "BIGINT";
        if (type == double.class || type == Double.class) return "DOUBLE";
        if (type == boolean.class || type == Boolean.class) return "BOOLEAN";
        if (type == Date.class) return "DATETIME";
        return "TEXT";
    }

    /**
     * 执行 SQL 更新语句，如 CREATE、INSERT、UPDATE、DELETE 等。
     *
     * @param sql 要执行的 SQL 更新语句。
     */
    private void executeUpdate(String sql) {
        try (Statement stmt = connection.createStatement()) {
            stmt.executeUpdate(sql);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 主键字段注解，用于标记实体类中的主键字段。
     */
    public @interface PrimaryKey {
        boolean autoIncrement() default false; //是否自增
    }

    /**
     * 自增字段注解，用于标记实体类中的自增字段。
     */
    public @interface AutoIncrement {}
}
